package com.icoolkj.ms.common.security.filter;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.icoolkj.ms.api.auth.bo.UserInfoInTokenBO;
import com.icoolkj.ms.api.auth.feign.TokenFeignClient;
import com.icoolkj.ms.api.rbac.feign.PermissionFeignClient;
import com.icoolkj.ms.common.core.constant.Auth;
import com.icoolkj.ms.common.core.constant.TokenConstants;
import com.icoolkj.ms.common.core.enums.ResponseEnum;
import com.icoolkj.ms.common.core.feign.FeignInsideAuthConfig;
import com.icoolkj.ms.common.core.handler.HttpHandler;
import com.icoolkj.ms.common.core.response.ServerResponseEntity;
import com.icoolkj.ms.common.core.utils.IpUtil;
import com.icoolkj.ms.common.security.AuthUserContext;
import com.icoolkj.ms.common.security.adapter.AuthConfigAdapter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
 * @author icoolkj
 * @version 1.0
 * @description 授权过滤，只要实现AuthConfigAdapter接口，添加对应路径即可：
 * @createDate 2025/02/09 17:29
 */
@Component
public class AuthFilter implements Filter {

    private static Logger logger = LoggerFactory.getLogger(AuthFilter.class);

    @Autowired
    private AuthConfigAdapter authConfigAdapter;

    @Autowired
    private HttpHandler httpHandler;

    @Autowired
    private FeignInsideAuthConfig feignInsideAuthConfig;

    @Autowired
    private TokenFeignClient tokenFeignClient;

    @Autowired
    private PermissionFeignClient permissionFeignClient;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        if (!feignRequestCheck(req)) {
            httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
            return;
        }

        if (Auth.CHECK_TOKEN_URI.equals(req.getRequestURI())) {
            chain.doFilter(req, res);
            return;
        }

        List<String> excludePathPatterns = authConfigAdapter.excludePathPatterns();

        // 如果匹配不需要授权的路径，就不需要校验是否需要授权
        if (CollectionUtil.isNotEmpty(excludePathPatterns)) {
            for (String excludePathPattern : excludePathPatterns) {
                AntPathMatcher pathMatcher = new AntPathMatcher();
                if (pathMatcher.match(excludePathPattern, req.getRequestURI())) {
                    chain.doFilter(req, res);
                    return;
                }
            }
        }

        String authorization = req.getHeader("Authorization");

        if (StrUtil.isBlank(authorization)) {
            httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
            return;
        }

        String accessToken = authorization.replaceFirst(TokenConstants.PREFIX, "");

        if (StrUtil.isBlank(authorization)) {
            httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
            return;
        }

        // 校验token，并返回用户信息
        ServerResponseEntity<UserInfoInTokenBO> userInfoInTokenVoServerResponseEntity = tokenFeignClient.checkToken(accessToken);
        if (!userInfoInTokenVoServerResponseEntity.isSuccess()) {
            httpHandler.printServerResponseToWeb(ServerResponseEntity.fail(ResponseEnum.UNAUTHORIZED));
            return;
        }

        UserInfoInTokenBO userInfoInToken = userInfoInTokenVoServerResponseEntity.getData();

        try {
            // 保存上下文
            AuthUserContext.set(userInfoInToken);

            chain.doFilter(req, res);
        }
        finally {
            AuthUserContext.clean();
        }


    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }

    private boolean feignRequestCheck(HttpServletRequest req) {
        // 不是feign请求，不用校验
        if (!req.getRequestURI().startsWith(FeignInsideAuthConfig.FEIGN_INSIDE_URL_PREFIX)) {
            return true;
        }
        String feignInsideSecret = req.getHeader(feignInsideAuthConfig.getKey());

        // 校验feign 请求携带的key 和 value是否正确
        if (StrUtil.isBlank(feignInsideSecret) || !ObjectUtil.equals(feignInsideSecret,feignInsideAuthConfig.getSecret())) {
            return false;
        }
        // ip白名单
        List<String> ips = feignInsideAuthConfig.getIps();
        // 移除无用的空ip
        ips.removeIf(StrUtil::isBlank);
        // 有ip白名单，且ip不在白名单内，校验失败
        if (CollectionUtil.isNotEmpty(ips) && !ips.contains(IpUtil.getCurrentRequestIp())) {
            logger.error("ip not in ip White list: {}, ip, {}", ips, IpUtil.getCurrentRequestIp());
            return false;
        }
        return true;
    }

}
